package cn.sc.summer.redis.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.sc.summer.constant.project.ProjectConstant;
import cn.sc.summer.redis.form.UpdateNacosServerInstanceForm;
import cn.sc.summer.redis.po.ServerInfo;
import cn.sc.summer.redis.po.ServiceInstance;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import cn.sc.summer.constant.util.BaseIpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * 类名：微服务实例工具类
 *
 * @author a-xin
 * @date 2023/12/20 10:25
 */
@Slf4j
@Component
public class NacosInstancesService {

    /**
     * 根据服务实例更新nacos配置信息
     *
     * @param form                     服务实例信息
     * @param nacosDiscoveryProperties nacos配置文件
     * @param <T>                      入参类型
     */
    public static <T extends ServiceInstance> void updateNacosInstance(T form, NacosDiscoveryProperties nacosDiscoveryProperties) {
        String[] serverGroupName = form.getServiceName().split("@@");
        String nacosUrl = createNacosUrl("/nacos/v1/ns/instance", nacosDiscoveryProperties);
        UpdateNacosServerInstanceForm updateNacosServerInstanceForm = new UpdateNacosServerInstanceForm();
        updateNacosServerInstanceForm.setIp(form.getIp());
        updateNacosServerInstanceForm.setPort(form.getPort());
        updateNacosServerInstanceForm.setServiceName(serverGroupName[1]);
        updateNacosServerInstanceForm.setClusterName(form.getClusterName());
        updateNacosServerInstanceForm.setGroupName(serverGroupName[0]);
        updateNacosServerInstanceForm.setEphemeral(form.getEphemeral());
        updateNacosServerInstanceForm.setWeight(form.getWeight() <= 0D ? 1D : form.getWeight());
        updateNacosServerInstanceForm.setEnabled(form.getEnabled());
        updateNacosServerInstanceForm.setMetadata(null);
        updateNacosServerInstanceForm.setNamespaceId(nacosDiscoveryProperties.getNamespace());
        String body = HttpRequest.put(nacosUrl).form(BeanUtil.beanToMap(updateNacosServerInstanceForm)).execute().body();
        if (!body.equals("ok")) {
            log.error("The information of the Nacos service: {} instance is abnormal!", serverGroupName[1]);
        } else {
            log.info("The information of the Nacos service: {} instance is updated!", serverGroupName[1]);
        }
    }

    /**
     * 获取nacos服务实例信息
     *
     * @param serviceName              服务名称
     * @param nacosDiscoveryProperties nacos配置文件
     * @return 返回数据
     */
    private static String getNacosInstanceInfo(String serviceName, NacosDiscoveryProperties nacosDiscoveryProperties) {
        String nacosUrl = createNacosUrl("/nacos/v1/ns/catalog/instances?", nacosDiscoveryProperties);
        ConcurrentMap<String, Object> params = new ConcurrentHashMap<>();
        params.put("serviceName", serviceName);
        params.put("clusterName", StrUtil.isBlank(nacosDiscoveryProperties.getClusterName()) ? "DEFAULT" : nacosDiscoveryProperties.getClusterName());
        params.put("groupName", nacosDiscoveryProperties.getGroup());
        params.put("pageSize", 100);
        params.put("pageNo", 1);
        params.put("namespaceId", nacosDiscoveryProperties.getNamespace());
        return HttpUtil.get(nacosUrl, params, 3000);
    }

    /**
     * 获取微服务所有存活实例IP+端口
     *
     * @param serviceName              服务名称
     * @param nacosDiscoveryProperties nacos配置
     * @return 存活实例IP+端口
     */
    public static List<String> getAllInstance(String serviceName, NacosDiscoveryProperties nacosDiscoveryProperties) {
        String resultInfo = getNacosInstanceInfo(serviceName, nacosDiscoveryProperties);
        try {
            Map<?, ?> resultMap = JSONObject.parseObject(resultInfo, Map.class);
            if (resultMap.get("count").toString().equals("0")) {
                return new ArrayList<>();
            } else {
                List<ServiceInstance> serviceInstanceList = JSONArray.parseArray(resultMap.get("list").toString(), ServiceInstance.class);
                List<String> healthInstances = serviceInstanceList.stream().filter(ServiceInstance::getHealthy)
                        .map(serviceInstance -> serviceInstance.getIp() + ":" + serviceInstance.getPort())
                        .collect(Collectors.toList());
                if (CollUtil.isEmpty(healthInstances)) {
                    return new ArrayList<>();
                } else {
                    return healthInstances;
                }
            }
        } catch (JSONException e) {
            log.error("==> The microservice: {}, catch an exception: {}", serviceName, resultInfo);
            return new ArrayList<>();
        }
    }

    /**
     * 获取所有服务信息
     *
     * @param nacosDiscoveryProperties nacos配置信息
     * @return 所有服务信息
     */
    public static List<ServerInfo> getAllServerInfo(NacosDiscoveryProperties nacosDiscoveryProperties) {
        String nacosUrl = createNacosUrl("/nacos/v1/ns/catalog/services", nacosDiscoveryProperties);
        ConcurrentMap<String, Object> params = new ConcurrentHashMap<>();
        params.put("hasIpCount", true);
        params.put("withInstance", false);
        params.put("pageNo", 1);
        params.put("pageSize", 100);
        params.put("serviceNameParam", null);
        params.put("groupNameParam", null);
        params.put("namespaceId", nacosDiscoveryProperties.getNamespace());
        String resultInfo = HttpUtil.get(nacosUrl, params, 3000);
        Map<?, ?> resultMap = JSONObject.parseObject(resultInfo, Map.class);
        if (resultMap.get("count").toString().equals("0")) {
            return new ArrayList<>();
        } else {
            return new ArrayList<>(JSONArray.parseArray(resultMap.get("serviceList").toString(), ServerInfo.class));
        }
    }

    /**
     * 获取微服务所有存活实例详细信息
     *
     * @param serviceName 服务名称
     * @return 存活实例IP+端口
     */
    public static List<ServiceInstance> getAllInstanceInfo(String serviceName, NacosDiscoveryProperties nacosDiscoveryProperties) {
        String resultInfo = getNacosInstanceInfo(serviceName, nacosDiscoveryProperties);
        try {
            Map<?, ?> resultMap = JSONObject.parseObject(resultInfo, Map.class);
            if (resultMap.get("count").toString().equals("0")) {
                return new ArrayList<>();
            } else {
                return JSONArray.parseArray(resultMap.get("list").toString(), ServiceInstance.class).stream().filter(ServiceInstance::getHealthy)
                        .collect(Collectors.toList());
            }
        } catch (JSONException e) {
            return new ArrayList<>();
        }
    }

    /**
     * 获取微服务实例个数
     *
     * @param serverName 服务名称
     * @return 存活实例个数
     */
    public static Integer getInstanceNum(String serverName, NacosDiscoveryProperties nacosDiscoveryProperties) {
        List<ServiceInstance> allInstanceInfo = getAllInstanceInfo(serverName, nacosDiscoveryProperties);
        return Math.toIntExact(allInstanceInfo.stream().filter(ServiceInstance::getHealthy)
                .count());
    }

    /**
     * 判断微服务是否存在存活的实例
     *
     * @param serverName 服务名称
     * @return 是否存在实例
     */
    public static Boolean containInstance(String serverName, NacosDiscoveryProperties nacosDiscoveryProperties) {
        return getInstanceNum(serverName, nacosDiscoveryProperties) > 0;
    }

    /**
     * 获取当前服务向nacos注册实例等价ip地址信息
     *
     * @return ip:port
     */
    public static String getServerNacosAddress() {
        String ipAddress = BaseIpUtil.getNacosInstanceIp();
        String port = SpringUtil.getApplicationContext().getEnvironment().getProperty("server.port");
        return ipAddress + ":" + port;
    }

    /**
     * 判断除当前服务外是否还存活服务实例
     *
     * @param address                  当前服务IP:port
     * @param serverName               服务名称
     * @param nacosDiscoveryProperties nacos配置项
     * @return 当前服务是否为最后一个服务
     */
    public static Boolean isLastInstance(String address, String serverName, NacosDiscoveryProperties nacosDiscoveryProperties) {
        List<String> allInstance = getAllInstance(serverName, nacosDiscoveryProperties);
        allInstance.remove(address);
        return allInstance.size() < 1;
    }

    /**
     * 判断除当前服务外是否还存活服务实例
     *
     * @return 当前服务是否为最后一个服务
     */
    public static Boolean isLastInstance() {
        NacosDiscoveryProperties nacosDiscoveryProperties = SpringUtil.getBean(NacosDiscoveryProperties.class);
        return NacosInstancesService.isLastInstance(NacosInstancesService.getServerNacosAddress(), ProjectConstant.SERVER_NAME, nacosDiscoveryProperties);
    }

    /**
     * 构造nacos请求地址信息
     *
     * @param nacosDiscoveryProperties nacos配置文件信息
     * @return url地址
     */
    private static String createNacosUrl(String url, NacosDiscoveryProperties nacosDiscoveryProperties) {
        StringBuilder stringBuilder = new StringBuilder("http://")
                .append(nacosDiscoveryProperties.getServerAddr())
                .append(url);
        String accessToken = NacosService.getAccessToken(nacosDiscoveryProperties);
        if (CharSequenceUtil.isNotBlank(accessToken)) {
            stringBuilder.append("accessToken=")
                    .append(accessToken);
        }
        return stringBuilder.toString();
    }

}
